#ifndef __ADDRESS_H__
#define __ADDRESS_H__

#include<memory>
#include<string>
#include<sys/socket.h>
#include<sys/types.h>
#include<iostream>
#include <arpa/inet.h>   //sockaddr_in
#include <sys/un.h>      //sockaddr_un
#include<netdb.h>        //addrinfo
#include<ifaddrs.h>      //getifaddrs, struct ifaddrs
#include<vector>
#include<map>            //multimap

namespace sylar{


class IPAddress;

class Address{
public:
    using ptr = std::shared_ptr<Address>;
    virtual ~Address(){}
    
    // 返回地址族协议
    int getFamily() const;

    // 根据host取出所有对应的addr
    static bool Lookup(std::vector<Address::ptr>& result, const std::string& host,
            int family = AF_INET, int type = 0, int protocol = 0);

    static Address::ptr LookupAny(const std::string& host,
            int family = AF_INET, int type = 0, int protocol = 0);
    static std::shared_ptr<IPAddress> LookupAnyIPAddress(const std::string& host,
            int family = AF_INET, int type = 0, int protocol = 0);

    // 通过网卡找地址
    /**
     *
     * @param result
     * @param family AF_UNSPEC  AF_INET
     * @return
     */
    static bool GetInterfaceAddresses(std::multimap<std::string
            ,std::pair<Address::ptr, uint32_t> >& result,
            int family = AF_UNSPEC);
    static bool GetInterfaceAddresses(std::vector<std::pair<Address::ptr, uint32_t> >&result
            ,const std::string& iface, int family = AF_INET);

    virtual const sockaddr* getAddr() const = 0;
    virtual sockaddr* getAddr()  = 0;
    
    virtual socklen_t getAddrLen() const = 0;
    // int转换未字符串地址
    virtual std::ostream& insert(std::ostream& os) const = 0;
    std::string toString() const;

    static Address::ptr Create(const sockaddr* addr, socklen_t addrlen);

    // 可以进行地址的比较，排序
    bool operator<(const  Address& rhs)const;
    bool operator==(const Address& rhs)const; 
    bool operator!=(const Address& rhs)const; 
};

class IPAddress : public Address{
public:
    using ptr = std::shared_ptr<IPAddress>;
    // 将str地址转换成目标地址, IPv4还是IPv6均可
    static IPAddress::ptr Create(const char* address, uint16_t port = 0);
    // 广播地址
    virtual IPAddress::ptr broadcastAddress(uint32_t prefix_len) = 0;
    // 网络地址
    virtual IPAddress::ptr networkAddress(uint32_t prefix_len) = 0;
    // 子网地址
    virtual IPAddress::ptr subnetMask(uint32_t prefix_len) = 0;

    
    virtual uint32_t getPort() const = 0;
    virtual void setPort(uint16_t v) = 0;
};

class IPv4Address:public IPAddress{
public:
    using ptr = std::shared_ptr<IPv4Address>;
    static IPv4Address::ptr Create(const char* address, uint16_t port = 0);

    IPv4Address(const sockaddr_in& address);
    IPv4Address(uint32_t address = INADDR_ANY,uint16_t port = 0);

    const sockaddr* getAddr() const  override;
    sockaddr* getAddr();
    socklen_t getAddrLen() const override;

    std::ostream& insert(std::ostream& os) const override;

    IPAddress::ptr broadcastAddress(uint32_t prefix_len) override;
    IPAddress::ptr networkAddress(uint32_t prefix_len) override;
    IPAddress::ptr subnetMask(uint32_t prefix_len) override;

    uint32_t getPort() const override;
    void setPort(uint16_t v) override;

private:
    sockaddr_in m_addr;
};

class IPv6Address:public IPAddress{
public:
    using ptr = std::shared_ptr<IPv6Address>;

    static IPv6Address::ptr Create(const char* address,uint16_t port = 0);

    IPv6Address();
    IPv6Address(const sockaddr_in6& address);
    IPv6Address(const uint8_t address[16], uint16_t port = 0);

    const sockaddr* getAddr() const  override;
    sockaddr* getAddr();
    socklen_t getAddrLen() const override;

    std::ostream& insert(std::ostream& os) const override;

    IPAddress::ptr broadcastAddress(uint32_t prefix_len) override;
    IPAddress::ptr networkAddress(uint32_t prefix_len) override;
    IPAddress::ptr subnetMask(uint32_t prefix_len) override;

    uint32_t getPort() const override;
    void setPort(uint16_t v) override;

private:
    sockaddr_in6 m_addr;
};

/**
 * @brief 本地套接字
 * 
 */
class UnixAddress : public Address{
public:
    using ptr = std::shared_ptr<UnixAddress>;

    UnixAddress();
    UnixAddress(const std::string& path);

    const sockaddr* getAddr() const  override;
    sockaddr* getAddr();
    socklen_t getAddrLen() const override;
    void setAddrLen(uint32_t v);

    std::ostream& insert(std::ostream& os) const override;

private:
    sockaddr_un m_addr;
    socklen_t m_length;
};

class UnknownAddress : public Address{
public:
    using ptr = std::shared_ptr<UnknownAddress>;

    UnknownAddress();
    UnknownAddress(int family);
    UnknownAddress(const sockaddr& addr);

    const sockaddr* getAddr() const  override;
    sockaddr* getAddr();
    socklen_t getAddrLen() const override;

    std::ostream& insert(std::ostream& os) const override;

private:
    sockaddr m_addr;
};

std::ostream& operator<<(std::ostream&, const Address& addr);

}


#endif